Listening to the Poke..

Listening to the Poke

Processing acceleration data from explicit simulation — and the difference between a good listener and a great one.

We poke systems to understand them. A child pushes the new toy. A mechanic taps the casting. And we drop a product onto simulated concrete, and ask one node — at the centre of mass of a block standing in for an engine we did not design and cannot see inside — to write down everything it felt.

Here is the idea underneath everything that follows. The answer is never the original poke. The system edits it. Every surface, every bond, every element in the mesh reflects it, scatters it, and rewrites it according to the system's structure, its state, and its history. What reaches you is the edited answer, and learning to read the edit is most of what analysis actually is.

This piece is the written companion to an audiobook of the same name. The recording is written for the ear, which means it moves at the speed of speech and cannot stop to show you a number, a table, or a line of code. This one stops. Where the recording says a thing is true, here you get the measurement that makes it true, and what it costs.

Every measurement quoted below was produced by a verification suite on the same test case: a 50 g half-sine of 6 ms duration, buried under 900 g of 22 kHz element ringing. Nothing here is asserted from memory.

Part One. The poke, and the edited answer

The edit arrives garbled in three distinct ways. You should be able to name all three, because each one gets a different response — and only one of the three is your fault.

The first: the solver's breathing

An explicit solver does not march through time at a steady pace. It advances only as far as the smallest, stiffest element will safely allow in a single step — the limit set by how long a stress wave takes to cross the tiniest element in the mesh. When an element deforms, or contact engages, or a sharp corner stiffens, the allowable step shrinks. The answer comes to you with an irregular heartbeat.

That is not corruption. That is the solver working hardest exactly where the most is happening. But every frequency tool ever built assumes an even clock, which is why restoring it is the first move of the craft and not an afterthought.

The second: the mesh's own voice

An elastic impact model is not a continuous solid. It is a box full of tiny springs and masses — a discrete approximation of a real continuum — and when you strike it, every one of those springs rings at its own natural frequency. That ringing is enormous, frequently larger than the physics you care about, and it lives at ferocious frequencies set by the size and stiffness of your elements rather than by anything you designed.

Your fifty-g impact arrives wearing a coat of fuzz a thousand g thick.

In the measured case used throughout this piece, that phrase is close to literal. A true 50 g half-sine with 22 kHz element ringing superimposed reads a peak of 901.88 g. The ringing is eighteen times the signal.

The third: your own sampling — and the one that cannot be undone

Suppose you asked the solver to write output only every so often, at some comfortable interval, because the full file seemed absurdly large. All that ferocious ringing does not disappear when you sample it coarsely. It folds. Every frequency too fast for your sampling rate reflects back down into the band you trust, wearing a disguise.

A hundred-kilohertz ring, lazily sampled, can masquerade as a smooth, gentle, one-kilohertz wave — indistinguishable from physics. Think of the wagon-wheel effect in an old movie: the spokes appear to rotate slowly, or even backward, because the camera sampled too slowly to catch the true motion. The wheel is not lying. The camera is simply too slow to tell the truth.

The rule, and the fine print that matters more. To capture a signal honestly you must sample it more than twice as fast as the fastest thing living inside it. Half your sampling rate is the ceiling, and that ceiling is the Nyquist frequency. Everything above it is not lost — it is folded back underneath and handed to you as a lower frequency that was never there.

But twice is the mathematical minimum. It is not an engineering target. Sample at twice and you may recover the frequency while completely missing the height of the peak. If you want a peak to still look like a peak, ten times is the honest number.

And aliasing is not like the other corruptions. Noise announces itself. Drift can be spotted. Clipping leaves flat-topped fingerprints. Aliasing leaves no evidence at all. Once it is in the record, no filter, no algorithm and no expert can remove it, because the record itself has become a plausible lie. There is no signature to detect and nothing to subtract.

The only defence is to never let it in. Capture every increment the solver computed, however large the file. Where output size genuinely forces your hand, use the run-time anti-aliasing filter that acts before data is written — not a coarser output interval. Storage is cheap; a confident wrong answer about a component going into a million devices is not.

Part Two. The good listener

A good listener, faced with a garbled message, does not panic and does not guess. They work in order — and the order is the craft. Each step protects against something the next step cannot fix.

#StepWhat it protectsWhat breaks if you skip it1Capture every solver incrementAliasing at the sourceIrreversible. No filter can remove it and nothing announces it.2Restore the clock — uniform grid at the finest step, linear interpolationSpectral validityFrequency tools assume even spacing. Higher-order interpolation invents overshoot at sharp events.3Guard filter, then decimateAliasing at the new rateThinning before filtering manufactures smooth fiction that looks like physics.4Analysis filter, zero-phaseNumerical conditioning, and peak timingIll-conditioned design on a fast record; a single pass shifts the peak.5Save as a new record, never overwriteReproducibilityYou cannot defend the number six months later, and the original is gone.

First: restore the clock

The raw output arrives with uneven time steps and most analysis tools assume equal spacing. So you regularize onto a steady time base at the solver's finest step, so that nothing which happened in those small, frantic moments gets averaged away.

And interpolate gently. A straight line between points; nothing fancier. Cubic and spline interpolation invent little overshoots near sharp events, and inventing is the one thing a listener must never do. On a shock record those invented overshoots land exactly where you are about to look for a peak.

Second: guard filter, then decimate

The record is enormous, and most of its bandwidth is mesh-song rather than physics. You are allowed to reduce it. But decimation is not deletion. Before you keep every tenth point, you must first quiet everything the smaller record cannot faithfully carry — a low-pass filter clamped down before the downsampling, so the high frequencies die honestly instead of folding down as aliases.

That pairing — filter, then thin — is what the word decimation properly means. Doing it in the other order is how smooth fiction gets manufactured.

Third: the analysis filter

There are two filters in this story and they are not the same filter. The guard filter rides inside decimation; its only job is to make sure nothing too fast gets thinned into a lie, and it is chosen from the new sample rate. The analysis filter comes afterwards; its job is to separate the impact you care about from the residue of ringing, and it is chosen from your question. Guard filter before the thinning, analysis filter after. Say it that way and the order never confuses you again.

Why does the analysis filter come after the thinning rather than before? A practical reason with teeth. A filter tuned to a few kilohertz, built against a ten-megahertz record, has a normalized cutoff on the order of one ten-thousandth. IIR designs become numerically ill-conditioned there — the mathematics crowd into a corner and quietly fall apart. The same filter built against the reduced record is exact.

This is not a matter of taste. Diehl, Carroll and Nagaraj documented precisely this failure in 1999 and 2000, noting that filtering or decimating explicit dynamic data typically requires extremely small normalized cutoff frequencies that cause significant numerical problems for common DSP programs. Thin first. Analyse second. Not preference — arithmetic.

Filtering up and down: what zero-phase actually is

Most treatments of this say: run the filter forward, then backward, so it steals no time. That is true, and it is the single most useful habit in this whole discipline. But stated bare it sounds like a trick, and engineers are right to be suspicious of tricks. Here is the mechanism.

Why every honest filter arrives late

A digital IIR filter is a recursion, and every term on the right-hand side is the present sample or an older one:

y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2]  -  a1*y[n-1] - a2*y[n-2]

There is no x[n+1] anywhere in that line, and there cannot be. The filter is looking exclusively at the past and making its best judgment about the present, which means its output always runs a little behind its input. That lag is not a defect in the design. It is what causality costs.

And it is worse than a clean shift, because a filter does not delay every frequency by the same amount. Content well below the cutoff comes through nearly on time; content up near the cutoff arrives noticeably late. So the pulse does not merely move. It smears, and it leans. The very shape you set out to measure changes on the way through.

Measured — 50 g half-sine, 6 ms, under 900 g of 22 kHz ringing, filtered at CFC 180:

RecordPeakTime of peakErrorTrue pulse (known)50.00 g13.000 ms—Raw (pulse + ringing)901.88 g10.400 msunusableOne pass — forward only50.07 g13.625 ms0.625 ms lateTwo passes — zero-phase49.92 g13.010 ms0.010 ms

Read that third row carefully, because it is the whole warning. The single pass recovered the amplitude beautifully — 50.07 g against a true 50.00 g — and moved the peak by 0.625 ms, better than ten percent of the pulse duration. The amplitude looked right, so nothing announced the error.

Does a fraction of a millisecond matter? Ask what you are about to do with the record. If you are comparing two channels to see whether the bracket amplified what the housing felt, the entire answer lives in the timing between them. If you are correlating a drop test against a simulation, you are lining up two peaks. If you are integrating to velocity change, you are trusting the shape. A filter that moves the peak corrupts all three, and it does so silently.

There is no backward filter

When people hear run the filter backward, they imagine a second filter — some mirror-image mathematics designed specially for the return trip. There is nothing of the kind. There is one filter, one set of coefficients, and an array that gets flipped end for end.

The reason it works follows directly from the recursion above. The filter is blind to the future, and that blindness is what makes it late. So hand it the record back to front. Its past is now your future, and the lag it introduces points the other way in real time.

Six moves:

  1. Design the filter once. One set of coefficients, computed from the cutoff and the sample rate. Both passes use identical numbers. There is no second design step.

  2. Pad both ends. Extend the record past each edge so the filter's startup transient dies in the padding rather than in your data. Odd padding — point reflection about each endpoint — suits shock records that do not start or end at zero.

  3. Run forward. First sample to last. The record comes out smooth, and late.

  4. Reverse the array. One line of code, and not one line of mathematics. This is the entire trick.

  5. Run the same filter again. Over the reversed record. It lags again — but a lag in a reversed record is a lead in the real one, by precisely the amount the first pass cost you.

  6. Reverse back, trim the padding. Done.

Ignoring the padding, that is two lines:

y = lfilter(b, a, x)                 # forward pass  -> smooth but LATE
y = lfilter(b, a, y[::-1])[::-1]     # reverse, filter again, un-reverse

That hand-rolled version and scipy.signal.filtfilt agree to 2.7 × 10-12 g across the interior of the record. They are the same operation. filtfilt simply handles the edges properly.

Why the phase cancels exactly, and not approximately

Reversing time flips the sign of every phase. The forward pass applies the filter's response; the backward pass applies its mirror image. Multiply the two together:

H(w) * conj(H(w))  =  |H(w)|^2

What survives is the magnitude squared — a real, non-negative quantity whose phase is zero at every frequency. Not small phase. None. This is not an approximation getting lucky; it is an identity, and it is why the peak sits precisely where the physics put it.

It is also, read the other way, the source of every cost below. The same line that zeroes the phase says the magnitude got squared.

What the second pass costs

The cutoff moves, and it moves down. A frequency attenuated to 0.707 on one pass comes out at 0.500 after two. The −3 dB point therefore shifts downward by a factor depending only on the single-pass order N:

f_-3dB(two passes) = f_design * 0.41421**(1/(2N))

N = 2  ->  0.802 * f_design          N = 4  ->  0.896 * f_design

Measured against a 2-pole Butterworth designed at 1000 Hz on a 200 kHz record: predicted 802.2 Hz, measured 801.5 Hz.

Which explains SAE J211's mysterious 2.0775. J211 specifies a 2-pole Butterworth applied forward then backward. The 2.0775 factor buried in its coefficient formula is deliberate pre-compensation: the single-pass design is made loose precisely so that after both passes the −3 dB point lands where the standard says it should. Verified on CFC 180 at 200 kHz — −3 dB sits at 373.5 Hz after one pass, and at 299.5 Hz after two. That is 1.664 × CFC, against the standard's specified 1.65 × CFC.

So follow J211 and you are compliant without thinking about it. Design your own Butterworth at your CFC frequency and run it twice, and you have silently moved your cutoff to about eighty percent of the number you typed — while still reporting the number you typed.

It is not causal, and it never will be. This filter uses the future to compute the present. That is perfectly legal on a record sitting in a file and completely impossible on a shaker controller or a data acquisition system running live. Zero-phase is a post-processing technique. Any request to port it to real time is a request to violate causality, and should be answered as such.

Both edges are disturbed, now from both directions. Padding suppresses the transient but does not abolish it. Treat the first and last few filter lengths of any zero-phase result as suspect, and never let a pulse of interest sit against the edge of your window.

Three traps worth checking in your own code

Trap 1 — J211 and SciPy disagree about signs, and about letters. J211 writes the recursion with positive feedback terms, and its names for the coefficient arrays are the reverse of SciPy's:

J211:   y[i] = a0*x[i] + a1*x[i-1] + a2*x[i-2] + b1*y[i-1] + b2*y[i-2]
SciPy:  y[i] = b0*x[i] + b1*x[i-1] + b2*x[i-2] - a1*y[i-1] - a2*y[i-2]

So the feedback coefficients must be negated and the arrays swapped: b_scipy = [a0, a1, a2] and a_scipy = [1, -b1, -b2]. Get this wrong and the filter is unstable and blows up immediately — which is the one mercy here, because it fails loudly rather than quietly.

Trap 2 — never run a zero-phase pass on the SRS oscillator. Ramp-invariant SRS coefficients are a different animal entirely. That recursion simulates a physical single-degree-of-freedom mass responding to base motion, and a real oscillator's response genuinely does lag its input. That lag is the physics, not an artefact. Filtering it forward and backward would produce a mass that begins moving before it is struck. Zero-phase belongs to the cleaning and analysis path only.

Trap 3 — dual-channel records must get identical treatment. Both channels take the same filter class, the same cutoff, and the same decimation target. Transmissibility and coherence are computed from the phase relationship between channels; filtering them differently manufactures a phase difference that is not in the data. Zero-phase filtering makes this safe rather than merely tolerable: because the phase distortion is identically zero on both channels, the relationship between them survives intact.

Choosing the cutoff: the derivative ladder

Choose the cutoff from a question, not a habit — because the metrics do not all suffer alike, and one simple rule sits underneath the whole picture. Every time you integrate, you divide by frequency. Every time you differentiate, you multiply by it. And mesh-song is nothing but frequency.

MetricOperation on the recordRinging scales asSensitivityVelocity change (ΔV)Integration1 / fRobust — barely notices itSRS below the ringing bandSDOF responserolls off above fnRobust in bandPeak accelerationNone — read rawf0, full amplitudeHigh — filter class must be statedPeak jerkDifferentiationfSevere — unusable unfiltered

It is worth being blunt about the third row, because it is commonly got wrong — often by grouping peak acceleration with velocity change as metrics that shrug off ringing. Velocity change is an integral, so the ringing is divided away on the road to ΔV. Peak acceleration is neither integrated nor differentiated. It reads the record raw and takes the ringing at full strength. In the measured case, raw peak reports 901.88 g against a true 50 g. The fifty-g impact in its thousand-g coat of fuzz reports the coat, not the impact.

This is the same reason CFC 60 and CFC 1000 applied to one drop record produce peaks differing by a factor of several — and both are correct under SAE J211, depending on what you are trying to learn. A peak g quoted without its filter class is not a measurement.

Jerk sits at the far end of the same ladder. It is a derivative, so it multiplies by frequency and amplifies precisely the band the ringing lives in. Quote jerk from an unfiltered explicit record and you are not quoting your product — you are quoting your mesh. And note the units are g/s: empirical screening thresholds of roughly 180,000 for glass, 100,000 for ceramics and 50,000 for PCB electronics are proxies, subordinate to a real test-to-failure limit for your specific stack-up the moment you have one.

Last: never destroy the original

Every cleaned, reduced, filtered version stands beside the raw record, not in place of it, with a note attached saying exactly what was done and why. The message and the listening notes are kept together — forever.

Part Three. The great listener

A good listener sees the message and is not bothered by the noise. But a great listener does not merely filter. A great listener turns to the noise itself and asks: why are you here? What are you trying to tell me?

Because in a deterministic simulation almost nothing is truly random. Every so-called artefact has a cause, and the cause is information.

The ringing was a review of your model, written by the model

That coat of element ringing you filtered away — before you discarded it, did you ask what it said? Its persistence tells you the model is elastically dominated: the energy of the impact has nowhere to die. And if the real product is damped as lightly as the model, the real product may ring too. And ring. And ring. Ringing is fatigue's favourite music.

Its frequency is a mesh diagnostic delivered free of charge. It tells you the size and stiffness of your elements, and whether your mesh can even carry the frequencies your fragility question needs. If you are asking about solder joint response at 3 kHz on a mesh whose own voice sits at 200 kHz, that is a very different conversation from a mesh ringing at 8 kHz.

When the classifier disagrees with you, write it down

A day on our own bench. A signal classifier looked at an explicit record and declared it random vibration. A mistake? Statistically, no. The sustained ringing genuinely dominated the record's energy, and the kurtosis statistic reported that faithfully while missing the transient underneath.

The great-listener response was not to silence the classifier. It was to let the engineer's knowledge outrank it and write the disagreement down — because that disagreement is itself a measurement. It tells you, in one line, how thoroughly the mesh-song covers the physics in this particular model.

The strangest one: a clock quantized into dust

Deep in a time column, we found the solver's steps apparently jumping between a few discrete values. It looked like adaptive stepping. It was not. It was the arithmetic of the computer itself: timestamps so large that the machine's number format could no longer tell fine moments apart.

What looked like solver behaviour was a message about provenance — about where the data had been and how it had been stored. Single or double precision. What units were chosen. How far the simulation had already travelled before this window. Filter that blindly and you learn nothing; interrogate it and you learn something true about your data's whole journey.

Clipping: the most candid confession in measurement

Flat tops, at identical levels. The instrument telling you plainly: I ran out of range, and the samples you most wanted were never written down.

No cleaning answers a confession. Only re-testing does. The great listener hears it, thanks the instrument for its honesty, and goes back for a better recording.

The discipline, in one line

Filter the noise out of the record — but never out of your notebook.

Remove it from the analysis. Keep it in the inquiry. Ask every artefact for its cause before you show it the door. The message tells you about your product. The noise tells you about your model, your instruments, and your data's history — the whole apparatus of listening. And only an engineer who understands the apparatus is entitled to trust the message.

The bench card

One page. Print it and put it where the drop tower is.

  1. Every increment, always. Aliasing has no cure and leaves no evidence.

  2. Twice is the mathematics. Ten times is the engineering.

  3. Restore the clock at the finest step, and interpolate with a straight line.

  4. Guard filter before the thinning. Analysis filter after.

  5. Filter forward and backward, or your peak is not where you think it is.

  6. Never quote peak g or jerk without stating the filter class.

  7. Filter the record. Never the notebook.

QuantityValueNoteJ211 two-pass −3 dB1.65 × CFCPre-compensated by the 2.0775 factor; verified at 1.664 × CFCTwo-pass cutoff shift, N = 20.802 × designApplies to any Butterworth you design yourselfAnti-alias sampling target10 × fmax2× recovers frequency only, not peak heightKurtosis classification> 3 shock, ≤ 3 randomSets artifact threshold: 15σ vs 4σJerk screening (g/s)180k / 100k / 50kGlass / ceramic / PCB. Proxies only — real test-to-failure supersedes

Joseph P. McFadden Sr. — McFaddenCAE.com. Written with Claude (Anthropic).

Filter and aliasing methodology after Diehl, T., Carroll, D. and Nagaraj, B., “Applications of DSP to Explicit Dynamic FEA Simulations of Elastically-Dominated Impact Problems,” Shock and Vibration 7(3), 2000, pp. 167–177; and Abaqus Users’ Conference, Chester, England, 1999. CFC filter definition per SAE J211-1 and ISO 6487. Shock response spectrum algorithms after Tom Irvine, vibrationdata.com — with thanks for a body of open work that made building on it possible.